/*
 * Copyright (c) 2009, Jan Pastor. All Rights Reserved.
 */

package sk.jp.imageio.jpeg;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import sk.jp.commons.exception.ApplicationException;
import sk.jp.commons.utils.ExceptionUtils;
import sk.jp.commons.utils.LogMsg;
import sk.jp.commons.utils.LogUtil;
import sk.jp.imageio.jpeg.metadata.Info;

/**
 * Implementation that supports lazy loading of picture data.
 */
public class JpegImageLazyImpl extends AbstractJpegImage {

    private static final Logger log = LoggerFactory.getLogger(JpegImageLazyImpl.class);

    private BufferedImage bufferedImage;
    private BufferedImage bufferedThumbnailImage;

    private final Object lock = new Object();

    //----------------------------------------------------------------------

    public JpegImageLazyImpl(Info info) {
        super(info);

        bufferedImage = null;
        bufferedThumbnailImage = null;
    }

    //----------------------------------------------------------------------

    public BufferedImage getBufferedImage() {
        log.trace(LogMsg.inp());
        synchronized (lock) {
            if(bufferedImage == null){
                if(log.isTraceEnabled()){
                    log.trace(LogUtil.getMemoryStateLogMessage());
                }
                invalidateThumbnail();

                File file = new File(getInfo().getFilePath());
                try {
                    bufferedImage = ImageUtil.loadPictureData(file);
                } catch (Exception e) {
                    throw new ApplicationException("Can't load picture data from file",
                                                   "sk.jp.jpeg-imageio.err.load",
                                                   ExceptionUtils.paramsToMap(
                                                       "file", getInfo().getFilePath()),
                                                   e);
                }
                if(log.isTraceEnabled()){
                    log.trace(LogUtil.getMemoryStateLogMessage());
                }
            }
            return bufferedImage;
        }
    }

    public BufferedImage getBufferedImage(Dimension dimension) {
        log.trace(LogMsg.inp("dimension", dimension));
        synchronized (lock) {
            return resize(getBufferedImage(), dimension);
        }
    }

    public BufferedImage getThumbnail(Dimension dimension) {
        log.trace(LogMsg.inp("dimension", dimension));
        synchronized (lock) {
            if(bufferedThumbnailImage == null){
                bufferedThumbnailImage = resize(getBufferedImage(), dimension);
                if(log.isTraceEnabled()){
                    log.trace(LogUtil.getMemoryStateLogMessage());
                }
            }
            return bufferedThumbnailImage;
        }
    }

    public void rotateLeft() {
        rotate(false);
    }

    public void rotateRight() {
        rotate(true);
    }

    public void free() {
        if(log.isDebugEnabled()){
            log.debug(LogMsg.inp());
        }
        synchronized(lock){
            bufferedImage = null;
        }
    }

    //----------------------------------------------------------------------

    private void rotate(boolean rotateClockwise){
        synchronized(lock){
            try {
                if(log.isTraceEnabled()){
                    log.trace(LogUtil.getMemoryStateLogMessage());
                }
                bufferedImage = ImageUtil.rotate(getBufferedImage(), rotateClockwise);
                bufferedThumbnailImage = null;   //invalidate thumbnail image
                if(log.isTraceEnabled()){
                    log.trace(LogUtil.getMemoryStateLogMessage());
                }
            } catch (Throwable e) {
                throw new ApplicationException("Can't rotate image",
                           "sk.jp.jpeg-imageio.err.rotate",
                           ExceptionUtils.paramsToMap("rotateClockwise", rotateClockwise,
                                                      "file", getInfo().getFilePath()), e);
            }
        }
    }

    private BufferedImage resize(BufferedImage bufferedImage, Dimension dimension){
        try {
            if(log.isTraceEnabled()){
                log.trace(LogUtil.getMemoryStateLogMessage());
            }
            BufferedImage image = ImageUtil.resize(bufferedImage, dimension);
            if(log.isTraceEnabled()){
                log.trace(LogUtil.getMemoryStateLogMessage());
            }
            return image;
        } catch (Throwable e) {
            throw new ApplicationException("Can't resize image",
                       "sk.jp.jpeg-imageio.err.resize",
                       ExceptionUtils.paramsToMap("dimension", dimension,
                                                  "file", getInfo().getFilePath()), e);
        }
    }

    private BufferedImage invalidateThumbnail() {
        synchronized (lock) {
            return bufferedThumbnailImage = null;
        }
    }

}
